# -*- coding: UTF-8 -*-
import hashlib
import logging
import os
import re
import yaml
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver import ActionChains
from webdriver_manager.chrome import ChromeDriverManager

"""
@author liuser
@date   21.12.26
"""

from selenium import webdriver
import time
import random
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
headers = {
    "Request-Source": "PC",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
}
delayTime   =   10

class twitter:

    # 获取随机一条评论
    def getRandComment(self):
        commentList = [
            'cool project',
            'thanks',
            '抽我',
            'This project is looks so innovative and impactful, happy to take participate in such huge project. You guys are very hard working and I am pretty sure you will reach to the Moon very soon. ',
            'I believe this project will be great. This project has its own characteristics so that it becomes an attraction for users. I hope this project will be successful in the future and will be the best.  ',
            'your project is very good,  I hope your project can be successful and successful in the future, I also hope the community can grow even bigger than now ',
        ]
        return random.choice(commentList)

    def randSleep(self, sleepTime=0):
        if sleepTime == 0:
            sleepTime = 5
        time.sleep(sleepTime)

    def __init__(self,path):
        self.config = {}
        option = webdriver.ChromeOptions()
        option.add_argument(r"user-data-dir=/Users/liuser/Library/Application Support/Google/Chrome/" + path)
        option.add_experimental_option('excludeSwitches', ['enable-automation'])  # 防止网站发现我们使用模拟器
        self.driver = webdriver.Chrome(ChromeDriverManager().install(),options=option)
        logging.basicConfig(level=logging.WARNING, filename=path+'.log')
        self.logger = logging.getLogger(__name__)
        # 保存正文和链接
        self.twitterRet = []
    def isLogin(self):
        '''
        判断是否登陆
        @return:
        '''
        url = 'https://twitter.com/liuser_cn'
        path = '/html/body/div[1]/div/div/div[2]/header/div/div/div/div[2]/div/div/div/div/div[2]/div/div[2]/div/div/div[4]/div'
        self.driver.get(url)
        self.randSleep()
        try:
            c = WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH, path)).text
        except NoSuchElementException:
            print('未获取到element')
            return True
        return c == None



    def run(self, element, retweetIds, comment=''):
        '''
        执行操作
        @param url: 要转发的地址
        @param retweetIds:  需要@的人，数组
        @param comment:     评论内容，不传，则自动获取
        @return:
        '''
        event = 'retweet'
        url = element['url']
        app.love(url)
        print("点赞完成，Done!")
        time.sleep(3)
        #是否需要关注
        text = element['text']
        followers = self.getfollowers(text)
        followers.append(element['name'])
        if len(followers) > 0:
            print('需要关注如下成员:')
            print(followers)
            self.follow(followers)
        else:
            print("不需要关注其他人")
        # 转推
        if event == 'retweet':
            print("触发:转推事件")
            app.retweet(url, retweetIds, comment)
            print("转推完成，Done!")
            time.sleep(3)
    def getfollowers(self,text):
        '''
        获取需要关注的总人数
        @param text: 推文
        @return:
        '''
        follows = []
        ret = re.findall(r'(\@\w+)', text)
        for follower in ret:
            follows.append(follower)
        return follows
    def love(self, url):
        time.sleep(10)
        # document.querySelector("#id__1ywj2ivyde7 > div:nth-child(3) > div > div > div > svg")
        # //*[@id="id__rudsnsx0adn"]/div[3]/div/div/div/svg
        self.driver.get(url)
        self.randSleep()
        try:
            xpath = '/html/body/div[1]/div/div/div[2]/main/div/div/div/div/div/div[2]/section/div/div/div[1]/div/div[1]/article/div/div/div/div[3]/div[5]/div/div[3]'
            # WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH, xpath)).click()
            self.retry(xpath)
        except NoSuchElementException:
            try:
                xpath = '/html/body/div[1]/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/section/div/div/div[1]/div/div/article/div/div/div/div[2]/div[2]/div[2]/div[3]/div/div[3]/div/div/div[1]'
                # self.driver.find_element(By.XPATH, xpath).click()
                WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH, xpath)).click()
            except :
                self.logger.warning("没匹配上，不知道什么妖魔鬼怪，记录下url:%s" % (url))
                return True
        time.sleep(3)
    def login(self):
        # 判断是否登陆
        isLoginRet = self.isLogin()
        if isLoginRet == False:
            print("已登陆")
            return True
        username =  self.config['username']
        pwd = self.config['pwd']
        uri = 'https://twitter.com/i/flow/login'
        self.driver.get(url=uri)
        self.randSleep()
        # 点击输入用户名
        print('输入用户名:' + username)
        usernamePath = '//*[@id="layers"]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[1]/div/div[5]/label/div/div[2]/div/input'
        WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH, usernamePath)).send_keys(username)
        WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH, '//*[@id="layers"]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[1]/div/div[6]')).click()
        print("输入密码" + pwd)
        pwdPath  = '//*[@id="layers"]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[1]/div/div[3]/div/label/div/div[2]/div[1]/input'
        WebDriverWait(self.driver,delayTime).until(lambda d: d.find_element(By.XPATH,pwdPath)).send_keys(pwd)
        time.sleep(15)
        WebDriverWait(self.driver,delayTime).until(lambda d: d.find_element(By.XPATH,'//*[@id="layers"]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[2]/div/div')).click()
        time.sleep(5)
        print('登陆完毕')

    # 关注
    def follow(self,beforeFollows):
        url = 'https://twitter.com/'
        for name in beforeFollows:
            fullUrl = url + name
            self.driver.get(fullUrl)
            self.randSleep()
            print("正在关注" + name)
            try:
                #可能会出现找不到元素，大概率是没有开私信，导致匹配不到
                followPath = '/html/body/div[1]/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div[2]/div[3]'
                # self.driver.find_element(By.XPATH, followPath).click()
                WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH,followPath)).click()

            except TimeoutException:
                followPath = '/html/body/div[1]/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div[2]/div[2]'
                try:
                    self.retry(followPath,3).click()
                except TimeoutError as e:
                    print(e)
                    pass
                # WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH,followPath))
            self.randSleep()
        print("关注用户结束")
        return True

    # 引用加转推
    def retweet(self, url=None, retweetIds=None, comment=None):
        xpath = '/html/body/div[1]/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/section/div/div/div[1]/div/div[1]/article/div/div/div/div[3]/div[5]/div/div[2]/div'
        print("进入详情")
        self.driver.get(url)
        time.sleep(5)
        # 评论引用
        print('弹出')
        try:
            # self.driver.find_element(By.XPATH, xpath).click()
            WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH, xpath)).click()

        except:
            return True
        time.sleep(5)
        retweetButton = '/html/body/div[1]/div/div/div[1]/div[2]/div/div/div/div[2]/div[3]/div/div/div/div'
        # 点击引用
        print('点击转推')
        # self.driver.find_element(By.XPATH, retweetButton).click()
        self.retry(retweetButton).click()
        # WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH, retweetButton)).click()

        time.sleep(5)
        print("准备评论")
        contentXpath = '/html/body/div[1]/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/section/div/div/div[1]/div/div[2]/div/div[2]/div[1]/div/div/div/div[2]/div[1]/div/div/div/div/div[1]/div/div/div/div/label/div[1]/div/div/div/div/div[2]/div/div/div/div'
        content = self.getRandComment() + '  '
        if retweetIds is not None:
            for retweetId in retweetIds:
                content +=  "@%s " % (retweetId)
            content += '\n'
        print("评论: " + content)
        # self.driver.find_element(By.XPATH, contentXpath).send_keys(content)
        WebDriverWait(self.driver, delayTime).until(lambda d: d.find_element(By.XPATH, contentXpath)).send_keys(content)

        # 发推
        self.randSleep()
        # self.driver.find_element(By.XPATH,).click()
        retweetPath = '/html/body/div[1]/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/section/div/div/div[1]/div/div[2]/div/div[2]/div[2]/div/div/div/div[2]/div[3]/div/div/div[2]/div[2]/div/span/span'
        WebDriverWait(self.driver,delayTime).until(lambda d: d.find_element(By.XPATH,retweetPath)).click()

    def retryForElements(self,path,retryCount=10,count=0):
        if count > retryCount:
            raise TimeoutError("尝试次数已达上限" + path)
        if count>0:
            print(path + "第%d次尝试" % (count))
        while True:
            try:
                return WebDriverWait(self.driver,delayTime).until(lambda d: d.find_elements(By.XPATH,path))
            except:
                self.retry(path,count+1)

    def retry(self,path,retryCount=10,count=0):
        if count > retryCount:
            raise TimeoutError("尝试次数已达上限" + path)
        if count>0:
            #模拟down
            self.down()
            print(path + "第%d次尝试" % (count))
        while True:
            try:
                return WebDriverWait(self.driver,delayTime).until(lambda d: d.find_element(By.XPATH,path))
            except:
                self.retry(path,count+1)

    def down(self):
        driver = self.driver
        for i in range(10):
            driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
            ActionChains(driver).key_down(Keys.DOWN).perform()
            time.sleep(5)



    def check(self, name):
        '''
        检测并返回获取到的最新数据
        @param name:
        @return:
        '''
        url = 'https://twitter.com/' + name
        self.driver.get(url)
        listPath  = '/html/body/div[1]/div/div/div[2]/main/div/div/div/div[1]/div/div[2]/div/div/div[2]/section/div/div/div'
        listRet   = self.retryForElements(listPath)
        # listRet = WebDriverWait(self.driver, delayTime).until(lambda d: d.find_elements(By.XPATH,listPath))
        for element1 in listRet:
            ret = {}
            # 正文
            try:
                contentPath = 'div/div/article/div/div/div/div[2]/div[2]/div[2]/div[1]'
                twitterContent = WebDriverWait(element1, delayTime).until(lambda d:d.find_element(By.XPATH, contentPath)).text
            except:
                #未获取到需要的数据，直接略过
                continue
            try:
                attrPath = 'div/div/article/div/div/div/div[2]/div[2]/div[1]/div/div/div[1]/a'
                url = WebDriverWait(element1, delayTime).until(lambda d:d.find_element(By.XPATH, attrPath)).get_attribute('href')
            except NoSuchElementException as e:
                print('发生异常' + e.msg)
                continue
            ret['text'] = twitterContent
            ret['url'] = url
            ret['name'] = name
            self.twitterRet.append(ret)


    def create(self,key,file,content):
        '''
        写入到文件
        @param key: 当前登录用户名
        @param file: 文件名，如果是保存已处理的推文，可以是  tweet
        @param content: 当前url
        @return:
        '''
        #目录是否存在
        currentFolderName = os.getcwd()
        folderName = currentFolderName+'/'+key
        fileName = folderName + '/' + file + '.txt'
        if  os.path.exists(folderName) == False:
            try:
                os.mkdir(r'{}'.format(folderName))
            except FileExistsError:
                pass

        with open(os.path.join(currentFolderName, fileName), 'a+') as f:
            hl = hashlib.md5()
            hl.update(content.encode(encoding='utf-8'))
            # print(hl.hexdigest())
            f.write(hl.hexdigest()+'\n')
    def read(self,key,file,content):
        '''
        从文件中读取
        @param key: 当前登录用户名
        @param file: 文件名，如果是保存已处理的推文，可以是  tweet
        @param content: 当前url
        @return:
        '''

        hl = hashlib.md5()
        hl.update(content.encode(encoding='utf-8'))
        md5Value = hl.hexdigest()
        #获取路径名称
        currentFolderName = os.getcwd()
        folderName = currentFolderName+'/'+key
        fileName = folderName + '/' + file + '.txt'
        try:
            for line in open(fileName):
                if line.strip() == md5Value.strip():
                    return True
        except FileNotFoundError:
            return False
        return False
    def checkRun(self,element):
        '''
        返回False 则执行，True则不执行
        @param element:
        @return:
        '''
        # 查询转发关键字,如果正文中出现以下关键字两次+，则认为可以转发
        allowKeywords = ['wl', 'giveaway', 'follow', 'retweet', 'rt','like','转推','点赞','喜欢','关注']
        # 如果出现以下关键字，则不参与
        refuseKeywords = ['discord', 'gleam.io']
        text = element['text']
        url = element['url']
        #检测是否已处理过
        if self.read(self.config['username'], 'tweet', url) == True:
            print('忽略:' + url)
            return False
        else:
            self.create(self.config['username'],'tweet',url)
        isBreak = 0
        text = text.strip().lower()
        # 如果黑名单命中，则略过
        for refuseKeyword in refuseKeywords:
            if text.find(refuseKeyword) > 0:
                isBreak = 1
        score = 0
        # 过滤空格，并且全部转换为小写
        for allowKeyword in allowKeywords:
            if text.find(allowKeyword) > 0:
                score += 1
        # 必须满足2个关键字，发触发动作
        if score < 2:
            isBreak = 1

        if isBreak == 1:
            return False
        return True
def loadConfig(filename):
    # 获取当前脚本所在文件夹路径
    curPath = os.path.dirname(os.path.realpath(__file__))
    # 获取yaml文件路径
    yamlPath = os.path.join(curPath, filename+'.yaml')
    f = open(yamlPath, 'r', encoding='utf-8')
    cfg = f.read()
    return yaml.safe_load(cfg)

path = os.path.basename(__file__).strip('.py')
app = twitter(path)
while True:
    #禁止使用协程、多线程....
    #accounts 是你想关注的博主，他们如果有新推文,就会跟随转推
    configYaml = loadConfig(path)
    config = {
        # 转推时@的人，填你自己的。
        'retweetIds': configYaml['retweetIds'],
        # 推特用户名
        'username': configYaml['username'],
        # 推特密码
        'pwd': configYaml['pwd'],
    }
    app.config = config
    app.login()
    accounts = configYaml['accounts']
    for account in accounts:
        print("获取" + account)
        #检测是否有推文
        app.check(account)
        print(app.twitterRet)
        for element in app.twitterRet:
            if app.checkRun(element) == True:
                app.run(element,config['retweetIds'])
    print('干完休息5分钟')
    time.sleep(120)